package com.cosmocoder.persistence.jpa;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Map;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.orm.jpa.support.JpaDaoSupport;

import com.cosmocoder.persistence.Dao;

public class JpaDao<EntityType, IdType extends Serializable> extends JpaDaoSupport implements Dao<EntityType, IdType>{
	
	Class<?> entityClass = null;
	
	@SuppressWarnings("unchecked")
	public Class<?> getEntityClass() {
		if(entityClass == null) {
			entityClass = (Class<EntityType>) (
					(ParameterizedType) getClass()
						.getGenericSuperclass()
					).getActualTypeArguments()[0];
		}
		return entityClass;
	}
	
	void setEntityClass (Class<?> entityClass) {
		//((ParameterizedType) getClass()..getGenericSuperclass()).getActualTypeArguments()[0] = entityClass;
		this.entityClass = entityClass;
	}
	
	@SuppressWarnings("unchecked")
	public EntityType get (IdType id) {
		return (EntityType) getJpaTemplate().getEntityManager().find(getEntityClass(), id);
	}
	
	
	public Collection<EntityType> getAll () {
		return getByCriteria();
	}
	
	public EntityType makePersistent(EntityType entity) {
		getJpaTemplate().getEntityManager().persist(entity);
		return entity;
	}

	public EntityType makeTransient(EntityType entity) {
		getJpaTemplate().getEntityManager().detach(entity);
		return entity;
	}
	
    @SuppressWarnings("unchecked")
    public Collection<EntityType> getByCriteria(Predicate... predicates) {
    	CriteriaBuilder queryBuilder = getJpaTemplate().getEntityManager().getCriteriaBuilder();
    	CriteriaQuery<EntityType> criteria = (CriteriaQuery<EntityType>) queryBuilder.createQuery(getClass());
    	criteria.from(getEntityClass());
    	criteria.where(predicates);
    	TypedQuery<EntityType> query = getJpaTemplate().getEntityManager().createQuery(criteria);
        return query.getResultList();
   }
   

	public void clear() {
		getJpaTemplate().getEntityManager().clear();
	}

	public void flush() {
		getJpaTemplate().getEntityManager().flush();
	}

	@SuppressWarnings("unchecked")
	public Collection<EntityType> getByProperty(String property, Object value) {
    	CriteriaBuilder criteriaBuilder = getJpaTemplate().getEntityManager().getCriteriaBuilder();
    	CriteriaQuery<EntityType> criteria = (CriteriaQuery<EntityType>) criteriaBuilder.createQuery(getClass());
		Root<EntityType> root = (Root<EntityType>) criteria.from(getEntityClass());
		return getByCriteria(criteriaBuilder.equal(root.get(property), value));
	}

	public Collection<EntityType> getByProperties(Map<String, Object> propetiesMap) {
		Predicate[] predicates = new Predicate[propetiesMap.size()];
		int currentCriterionIndex = 0;
		for(Map.Entry<String, Object> propertyEntry : propetiesMap.entrySet()) {
			predicates[currentCriterionIndex] = parsePropertyEntry(propertyEntry);
			currentCriterionIndex ++;
		}
		
		return getByCriteria(predicates);
		
	}
	
	@SuppressWarnings("unchecked")
	private Predicate parsePropertyEntry(Map.Entry<String, Object> propertyEntry) {
		CriteriaBuilder criteriaBuilder = getJpaTemplate().getEntityManager().getCriteriaBuilder();
    	CriteriaQuery<EntityType> criteria = (CriteriaQuery<EntityType>) criteriaBuilder.createQuery(getClass());
    	Root<EntityType> root = (Root<EntityType>) criteria.from(getEntityClass());
    	return criteriaBuilder.equal(root.get(propertyEntry.getKey()), propertyEntry.getValue());
	}
}
